package controller;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.event.ChangeEvent;

import model.csExceptions.NotEnoughGrenades;
import model.csExceptions.TooMuchGrenades;
import model.enumTypes.CSGrenadeType;
import model.events.CSGrenadeEvent;
import model.events.CSLOSEvent;
import model.path.CSPlayerPath;
import model.world.CSPlayer;
import model.world.CSWorld;
import parameters.GUIParameters;
import view.ImagePoint2D;
import view.main.MainPanel;
import view.modifiers.GrenadeEventModifier;
import view.modifiers.LOSEventModifier;
import view.modifiers.Modifier;
import view.modifiers.PlayerPathModifier;
import view.modifiers.ShiftModifier;
import view.player.GrenadeCountPanel;
import view.player.PlayerPanel;
import constants.CSConstants;
import constants.Icons;




public class CSController extends CompleteListener {

	private MainPanel gui;
	private CSWorld world;
	
	private CSControllerState state;
	private boolean clicked;
	private boolean playing;
	private boolean filterActive;
	
	// Selected player's index :
	private int selectedPlayerIndex;
	// Selected entity type :
	private Object selectedEntity;
	// Selected node's index in the selected path :
	private int selectedIndex;
	
	// Playing thread :
	private CSPlayThread playThread;
	
	public CSController(MainPanel gui_, File mapFile) {
		
		selectedIndex = 0;
		selectedEntity = null;
		selectedPlayerIndex = -1;
		gui = gui_;
		playing = false;
		filterActive = false;
		
		// PARSING THE MAP FILE :
		BufferedReader reader = null;
		
		List<Point> spawns = new ArrayList<Point>();
		
		try {
			reader = new BufferedReader(new FileReader(mapFile));
			// Parsing for spawns :
			String current = reader.readLine();
			while (current != null && current.length()!=0
					&& current.contains(",")) {
				spawns.add(new Point(Integer.parseInt(current.split(",")[0]),
						Integer.parseInt(current.split(",")[1])));
				current = reader.readLine();
			}
			if (current == null){
			    throw new IOException();
			}
			GUIParameters.setScale(Double.parseDouble(current));
		} catch (NumberFormatException e) {
		    System.out.println("MALFORMED FILE");
		} catch (IOException ioe) {
            System.err.println("Error while reading file: " + ioe);
		} finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (final IOException ioe) {
                    System.out.println("Error while closing reader in CSController: " + ioe);
                }
            }
        }
		
		
		world = new CSWorld(gui.getTimelinePan().getEndTime(),spawns);
		
		List<ImagePoint2D> temp = new ArrayList<ImagePoint2D>();
		for (int i=0; i<5; i++)
			temp.add(new ImagePoint2D(spawns.get(i),Icons.getIcon("player")));
		
		gui.getWorldPan().updateDynamicLists(temp, null);
		state = CSControllerState.IDLE;
		clicked = false;
	}
	
	public CSWorld getWorld() {
		return world;
	}
	
	public void notifySelectedChange() {
		selectedPlayerIndex = -1;
		selectedEntity = null;
		selectedIndex = -1;
		List<Line2D> list = world.getPlayer(gui.getSelected()).getPath().getLine2DArrayList();
		gui.getWorldPan().updateSpecialLists(list == null ? new ArrayList<Line2D>() : list,
				null,
				null);
		completeStaticUpdate();
		stateChanged(null);
	}
	
	public void notifyPlayEnd() {
		gui.setEnabled(true);
		gui.getTimelineCommandPan().setPlaying(false);
		completeStaticUpdate();
		gui.getWorldPan().updateDynamicLists(null, new ArrayList<ImagePoint2D>());
		stateChanged(null);
	}
	
	public void notifyNameChange() {
		for (int i=0; i<5; i++)
			world.getPlayer(i).setName(gui.getPlayerPan()[i].getPlayersName());
		stateChanged(null);
	}
	
	public void notifyDeath() {
		if (playThread == null)
			return;
		
		if (playThread.isAlive())
			playThread.interrupt();
	}
	
	public boolean updateWorldAndGUI(double deltaTime) {
		gui.getTimelinePan().setCurrentTime(gui.getTimelinePan().getCurrentTime()+deltaTime);
		
//		synchronized (this) {
//			try {
//				wait();
//			}
//			catch (Exception e) {}
//		} // TODO TODO TODO
		
		return gui.getTimelinePan().getCurrentTime()!=gui.getTimelinePan().getEndTime();
	}
	
	public void completeStaticUpdate() {
		// Updating the GUI :
		CSPlayer player = world.getPlayer(gui.getSelected());
		List<Line2D> temp1 = new ArrayList<Line2D>(); // PATHS
		List<Line2D> temp2 = new ArrayList<Line2D>(); // LOS
		List<Line2D> temp3 = new ArrayList<Line2D>(); // GRENADES
		// Paths :
		for (int i = filterActive ? gui.getSelected() : 0;
			 i<(filterActive ? gui.getSelected()+1 : 5);
			 i++)
			temp1.addAll(world.getPlayer(i).getPath().getLine2DArrayList());
		
		// LOS events :
		for (int i = filterActive ? gui.getSelected() : 0;
			 i<(filterActive ? gui.getSelected()+1 : 5);
			 i++) {
			List<CSLOSEvent> los = world.getPlayer(i).getPath().getLosEvents();
			for (int j=0; j<los.size(); j++)
				temp2.add(new Line2D.Float(los.get(j).getStartPosition(),los.get(j).getPosition()));
		}
		
		// Grenade events :
		for (int i = filterActive ? gui.getSelected() : 0;
			 i<(filterActive ? gui.getSelected()+1 : 5);
			 i++) {
			List<CSGrenadeEvent> events = world.getPlayer(i).getPath().getGrenadeEvents();
			for (int k=0; k<events.size(); k++)
				temp3.addAll(world.getPlayer(i).getPath().getGrenadeEvents().get(k).getLine2DArrayList());
		}
		
		gui.getWorldPan().updateStaticLists(
				temp1,
				temp3, temp2);
		gui.getWorldPan().updateSpecialLists(player.getPath().getLine2DArrayList(), null, null);
		gui.getWorldPan().repaint();
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		// *****************
		// *COMMAND BUTTONS*
		// *****************
		// The Filter button :
		if (e.getActionCommand().equals("filter")) {
			filterActive = !filterActive;
			completeStaticUpdate();
			stateChanged(null);
		}
		if (e.getActionCommand().equals("nextNode")) {
			CSPlayer current = world.getPlayer(gui.getSelected());
			gui.getTimelinePan().setCurrentTime(current.getPath().getNextNodeTime());
		}
		if (e.getActionCommand().equals("waitTill")) {
			ShiftModifier mod = null;
			if (selectedEntity instanceof CSPlayerPath)
				mod = new ShiftModifier((CSPlayerPath)selectedEntity,selectedIndex);
			if (mod == null)
				JOptionPane.showMessageDialog(gui, new JLabel("<html>Please select a Path node first !<br>"+
						"<i>(the shift will be applied on the selected Path node)</i>"));
			else {
				JOptionPane.showMessageDialog(gui, mod);
				mod.modify();
				completeStaticUpdate();
				stateChanged(null);
			}
		}
		
		// *************************
		// *SIMPLE TIMELINE BUTTONS*
		// *************************
		// The Goto button :
		if (e.getActionCommand().equals("go")) {
			gui.getTimelinePan().setCurrentTime(gui.getTimeJumpPan().getTime());
			return;
		}
		// The Start button :
		if (e.getActionCommand().equals("start")) {
			gui.getTimelinePan().setCurrentTime(0);
			return;
		}
		// The End button :
		if (e.getActionCommand().equals("end")) {
			gui.getTimelinePan().setCurrentTime(gui.getTimelinePan().getEndTime());
			return;
		}
		// The Update button :
		if (e.getActionCommand().equals("update")) {
			for (int i=0; i<5; i++) {
				world.getPlayer(i).getPath().setEndTime(gui.getTimelinePan().getEndTime());
			}
		}
		
		// *************************
		// *GRENADE ADD/DEL BUTTONS*
		// *************************
		// An add FB button :
		if (e.getActionCommand().equals("addFB")) {
			GrenadeCountPanel source = (GrenadeCountPanel)((JComponent)e.getSource()).getParent();
			CSPlayer player = world.getPlayer(((PlayerPanel)source.getParent().getParent().getParent()).getId());
			try {
				player.giveFB();
				source.incCount();
			}
			catch (TooMuchGrenades exc) {
			}
			return;
		}
		// A del FB button :
		if (e.getActionCommand().equals("delFB")) {
			GrenadeCountPanel source = (GrenadeCountPanel)((JComponent)e.getSource()).getParent();
			CSPlayer player = world.getPlayer(((PlayerPanel)source.getParent().getParent().getParent()).getId());
			try {
				player.throwFB();
				source.decCount();
			}
			catch (NotEnoughGrenades exc) {
			}
			return;
		}
		// An add HE button :
		if (e.getActionCommand().equals("addHE")) {
			GrenadeCountPanel source = (GrenadeCountPanel)((JComponent)e.getSource()).getParent();
			CSPlayer player = world.getPlayer(((PlayerPanel)source.getParent().getParent().getParent()).getId());
			try {
				player.giveHE();
				source.incCount();
			}
			catch (TooMuchGrenades exc) {
			}
			return;
		}
		// A del HE button :
		if (e.getActionCommand().equals("delHE")) {
			GrenadeCountPanel source = (GrenadeCountPanel)((JComponent)e.getSource()).getParent();
			CSPlayer player = world.getPlayer(((PlayerPanel)source.getParent().getParent().getParent()).getId());
			try {
				player.throwHE();
				source.decCount();
			}
			catch (NotEnoughGrenades exc) {
			}
			return;
		}
		// An add SG button :
		if (e.getActionCommand().equals("addSG")) {
			GrenadeCountPanel source = (GrenadeCountPanel)((JComponent)e.getSource()).getParent();
			CSPlayer player = world.getPlayer(((PlayerPanel)source.getParent().getParent().getParent()).getId());
			try {
				player.giveSG();
				source.incCount();
			}
			catch (TooMuchGrenades exc) {
			}
			return;
		}
		// A del SG button :
		if (e.getActionCommand().equals("delSG")) {
			GrenadeCountPanel source = (GrenadeCountPanel)((JComponent)e.getSource()).getParent();
			CSPlayer player = world.getPlayer(((PlayerPanel)source.getParent().getParent().getParent()).getId());
			try {
				player.throwSG();
				source.decCount();
			}
			catch (NotEnoughGrenades exc) {
			}
			return;
		}
		// ********************************
		// *END OF GRENADE ADD/DEL BUTTONS*
		// ********************************
		if (e.getActionCommand().equals("fullStuff")) {
			PlayerPanel source = (PlayerPanel)((JComponent)e.getSource()).getParent().getParent();
			CSPlayer player = world.getPlayer(source.getId());
			player.setFBs(2);
			player.setHEs(1);
			player.setSGs(1);
			source.getFbPan().setCount(2);
			source.getHePan().setCount(1);
			source.getSgPan().setCount(1);
			return;
		}
		if (e.getActionCommand().equals("noStuff")) {
			PlayerPanel source = (PlayerPanel)((JComponent)e.getSource()).getParent().getParent();
			CSPlayer player = world.getPlayer(source.getId());
			player.setFBs(player.getPath().getFBEventsCount());
			player.setHEs(player.getPath().getHEEventsCount());
			player.setSGs(player.getPath().getSGEventsCount());
			source.getFbPan().setCount(player.getPath().getFBEventsCount());
			source.getHePan().setCount(player.getPath().getHEEventsCount());
			source.getSgPan().setCount(player.getPath().getSGEventsCount());
			return;
		}
		
		// *****************
		// * STATE BUTTONS *
		// *****************
		
		// MOVES :
		if (e.getActionCommand().equals("addMove")) {
			if (state == CSControllerState.ADDINGMOVE) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.ADDINGMOVE;
				gui.setAddingMove();
			}
			return;
		}
		if (e.getActionCommand().equals("insMove")) {
			if (state == CSControllerState.INSERTINGMOVE) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.INSERTINGMOVE;
				gui.setInsertingMove();
			}
			return;
		}
		if (e.getActionCommand().equals("delMove")) {
			if (state == CSControllerState.DELETINGMOVE) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.DELETINGMOVE;
				gui.setDeletingMove();
			}
			return;
		}
		
		// GRES :
		if (e.getActionCommand().equals("addGre")) {
			if (state == CSControllerState.ADDINGGRE) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.ADDINGGRE;
				gui.setAddingGre();
			}
			return;
		}
		if (e.getActionCommand().equals("delGre")) {
			if (state == CSControllerState.DELETINGGRE) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.DELETINGGRE;
				gui.setDeletingGre();
			}
			return;
		}
		
		// LOS
		if (e.getActionCommand().equals("addLOS")) {
			if (state == CSControllerState.ADDINGLOS) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.ADDINGLOS;
				gui.setAddingLOS();
			}
			return;
		}
		if (e.getActionCommand().equals("delLOS")) {
			if (state == CSControllerState.DELETINGLOS) {
				state = CSControllerState.IDLE;
				gui.setIdle();
			}
			else {
				state = CSControllerState.DELETINGLOS;
				gui.setDeletingLOS();
			}
			return;
		}
		// **************
		// * PLAY/PAUSE *
		// **************
		
		// PLAY :
		if (e.getActionCommand().equals("play")) {
			playThread = new CSPlayThread(33,this);
			new Thread(playThread).start();
			gui.setEnabled(false);
			playing = true;
		}
		if (e.getActionCommand().equals("pause")) {
			playThread.interrupt();
			gui.setEnabled(true);
			playing = false;
		}
		
		// *************************
		// * OPEN/SAVE/QUIT/EXPORT *
		// *************************
		
		// SAVE :
		if (e.getActionCommand().equals("save")) {
			JFileChooser fc = new JFileChooser();
			fc.showSaveDialog(gui);
			File chosenFile = fc.getSelectedFile();
			
			if (chosenFile == null)
				return;
			
			String sf = chosenFile.toString();
			if (!sf.endsWith(".strat"))
				chosenFile = new File(sf+".strat");
			
			try {
				FileOutputStream file = new FileOutputStream(chosenFile);
				ObjectOutputStream output = new ObjectOutputStream(file);
				
				output.writeObject(world);
				
				output.close();
			}
			catch (IOException exc) {}
		}
		if (e.getActionCommand().equals("open")) {
			JFileChooser fc = new JFileChooser();
			fc.showOpenDialog(gui);
			File chosenFile = fc.getSelectedFile();
			
			if (chosenFile == null)
				return;
			
			String sf = chosenFile.toString();
			if (!sf.endsWith(".strat"))
				return;
			
			try {
				FileInputStream file = new FileInputStream(chosenFile);
				ObjectInputStream input = new ObjectInputStream(file);
				
				world = (CSWorld)input.readObject();
				completeStaticUpdate();
				stateChanged(null);
				gui.getWorldPan().repaint();
				
				for (int i=0; i<5; i++)
					gui.getPlayerPan()[i].setPlayersName(world.getPlayer(i).getName());
				
				input.close();
			}
			catch (Exception exc) {}
		}
		if (e.getActionCommand().equals("close")) {
			gui.windowClosing(null);
		}
		if (e.getActionCommand().equals("screen")) {

			// CLEARING THE INACTIVE EVENTS :
			gui.getWorldPan().updateStaticLists(new ArrayList<Line2D>(), new ArrayList<Line2D>(), new ArrayList<Line2D>());
			gui.getWorldPan().updateSpecialLists(new ArrayList<Line2D>(), null, null);
			// Displaying grenades :
			List<ImagePoint2D> temp = new ArrayList<ImagePoint2D>();
			double time = gui.getTimelinePan().getCurrentTime();
			for (int i=0; i<5; i++) {
				CSGrenadeEvent current = world.getPlayer(i).getPath().getGrenadeEventAt(time);
				ImageIcon icon = Icons.getIcon("err");

				if (current != null) {
					if (current.getType() == CSGrenadeType.HE)
						icon = Icons.getIcon("he");
					if (current.getType() == CSGrenadeType.FB)
						icon = Icons.getIcon("fb");
					if (current.getType() == CSGrenadeType.SG)
						icon = Icons.getIcon("sg");
					temp.add(new ImagePoint2D(
							current.getPositionAtTime(time+CSConstants.GRENADE_TIMEOUT),
							icon));
				}
			}
			gui.getWorldPan().updateDynamicLists(null, temp);
			gui.getWorldPan().repaint();

			JFileChooser fc = new JFileChooser();
			fc.showSaveDialog(gui);
			File chosenFile = fc.getSelectedFile();

			if (chosenFile != null) {

				String sf = chosenFile.toString();
				if (!sf.endsWith(".jpg"))
					chosenFile = new File(sf+".jpg");

				BufferedImage img = new BufferedImage(gui.getWorldPan().getWidth(),
						gui.getWorldPan().getHeight(),
						BufferedImage.TYPE_INT_RGB);
				Graphics2D g2 = img.createGraphics();
				gui.getWorldPan().paint(g2);
				g2.dispose();
				try {
					ImageIO.write(img, "jpg", chosenFile);
				}
				catch (IOException ioe) {
					System.out.println("io error: " + ioe.getMessage());
				}
			}
			
			gui.getWorldPan().updateDynamicLists(null, new ArrayList<ImagePoint2D>());
			completeStaticUpdate();
			stateChanged(null);
		}
	}

	@Override
	public void mouseClicked(MouseEvent e) {}
	@Override
	public void mouseDragged(MouseEvent e) {
		if (state == CSControllerState.IDLE) {
			Point selected = gui.getWorldPan().getSelected();
			if (selected != null) {
				selected.setLocation(e.getPoint());
				if (selectedEntity instanceof CSPlayerPath)
					((CSPlayerPath)selectedEntity).moveNode(selectedIndex, e.getPoint());
				else if (selectedEntity instanceof CSLOSEvent)
					((CSLOSEvent)selectedEntity).setPosition(e.getPoint());
				else if (selectedEntity instanceof CSGrenadeEvent)
					((CSGrenadeEvent)selectedEntity).movePoint(selectedIndex, e.getPoint());

				completeStaticUpdate();
				gui.getWorldPan().repaint();
				stateChanged(null);
			}
		}
	}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}
	@Override
	public void mouseMoved(MouseEvent e) {}
	@Override
	public void mousePressed(MouseEvent e) {
		CSPlayer player = world.getPlayer(gui.getSelected());
		// If state = IDLE, select something if possible :
		if (state == CSControllerState.IDLE) {
			// Caclulating the selected point :
			int index = player.getPath().getPossibleSelectedNode(e.getPoint());
			if (index == -1) {
				List<CSGrenadeEvent> list = player.getPath().getGrenadeEvents();
				int i=0;
				while (i<list.size() && index == -1) {
					index = list.get(i).getPossibleSelectedIndex(e.getPoint());
					i++;
				}
				i--;
				if (index == -1) {
					List<CSLOSEvent> list2 = player.getPath().getLosEvents();
					for (int j=0;j<list2.size();j++) {
						if (list2.get(j).isSelectable(e.getPoint())) {
							// Giving the GUI the position of the selected node :
							gui.getWorldPan().setSelected(list2.get(j).getPosition());
							// Giving the Controller the mean to control the selected
							// node :
							selectedPlayerIndex = gui.getSelected();
							selectedEntity = player.getPath().getLosEvents().get(j);
							selectedIndex = 0;
						}
					}
				}
				else {
					gui.getWorldPan().setSelected(player.getPath().getGrenadeEvents().get(i).getNode(index));
					selectedPlayerIndex = gui.getSelected();
					selectedEntity = player.getPath().getGrenadeEvents().get(i);
					selectedIndex = index;
				}
			}
			else {
				gui.getWorldPan().setSelected(player.getPath().getNode(index));
				selectedPlayerIndex = gui.getSelected();
				selectedEntity = player.getPath();
				selectedIndex = index;
			}
		}
	}

	@Override
	public void mouseReleased(MouseEvent e) {
		CSPlayer player = world.getPlayer(gui.getSelected());
		// Left click or any other button ?
		// If right click :
		if (e.getButton() == 1) {
			// TESTS ON THE STATE :
			// If we are adding a Move :
			if (state == CSControllerState.ADDINGMOVE) {
				if (gui.getMovePan().getWaitTime()==-1) {
					JOptionPane.showMessageDialog(gui,
							new JLabel("The wait time has to be an integer value."));
					return;
				}

				player.getPath().appendNode(e.getPoint(),
						gui.getMovePan().getMoveStyle(),
						gui.getMovePan().getWaitTime());

				completeStaticUpdate();
				stateChanged(null); // To avoid a little problem you'll probably find by yourself.
			}
			// If we are Inserting a Move (DO NOT WORK YET)
			if (state == CSControllerState.INSERTINGMOVE) {
			    // TODO TODO TODO TODO TODO TODO TODO TODO TODO
			}
			// If we are deleting a Move :
			if (state == CSControllerState.DELETINGMOVE) {
				int index = player.getPath().getPossibleSelectedNode(e.getPoint());
				if (index != -1) {
					player.getPath().deleteNode(index);

					List<Line2D> temp = new ArrayList<Line2D>();
					for (int i=0; i<5; i++)
						temp.addAll(world.getPlayer(i).getPath().getLine2DArrayList());

					gui.getWorldPan().updateStaticLists(temp,
							null, null);
					gui.getWorldPan().updateSpecialLists(player.getPath().getLine2DArrayList(), null, null);
					
					completeStaticUpdate();
					gui.getWorldPan().repaint();
					stateChanged(null);
				}
			}
			// If we are adding a LOS event :
			if (state == CSControllerState.ADDINGLOS) {
				if (gui.getLosPan().getWaitTime()==-1) {
					JOptionPane.showMessageDialog(gui,
							new JLabel("The wait time has to be an integer value."));
					return;
				}
				double time = gui.getTimelinePan().getCurrentTime();
				if (player.getPath().getLOSEventsAt(time) == null) {
					player.getPath().addLOSEvent(
							new CSLOSEvent(e.getPoint(),
									player.getCurrentPosition(),
									time,
									time+gui.getLosPan().getWaitTime(),
									player.getPath()));
					
					List<Line2D> temp = gui.getWorldPan().getLosEvents();
					temp.add(new Line2D.Float(player.getCurrentPosition(),e.getPoint()));
					gui.getWorldPan().updateStaticLists(null, null, temp);
					stateChanged(null);
				}
				else
					JOptionPane.showMessageDialog(gui,
							new JLabel("You cannot create two LOS events at the same date."));
			}
			// If we are deleting a LOS event :
			if (state == CSControllerState.DELETINGLOS) {
				int losEventsNumber = world.getPlayer(gui.getSelected()).getPath().getLosEvents().size();
				CSPlayerPath path = world.getPlayer(gui.getSelected()).getPath();
				for (int i=0; i<losEventsNumber; i++)
					// Removing from model :
					if (path.getLosEvents().get(i).isSelectable(e.getPoint()))
						path.removeLOSEvent(path.getLosEvents().get(i).getStartTime());

				// Updating the GUI :
				List<Line2D> temp = new ArrayList<Line2D>();
				for (int i=0; i<5; i++) {
					List<CSLOSEvent> los = world.getPlayer(i).getPath().getLosEvents();
					for (int j=0; j<los.size(); j++)
						temp.add(new Line2D.Float(los.get(j).getStartPosition(),los.get(j).getPosition()));
				}

				gui.getWorldPan().updateStaticLists(null,null,temp);
				stateChanged(null);
			}
			// If we are adding a Gre event :
			if (state == CSControllerState.ADDINGGRE) {
				// This is the case when we are adding a new event :
				double time = gui.getTimelinePan().getCurrentTime();
				if (player.getPath().isGrenadeEventsPossibleAt(time)) {
					boolean possible;
					int greTypeIndex = gui.getGrePan().getGreType();
					CSGrenadeType greType;
					switch (greTypeIndex) {
					case 0:
						possible = player.canThrowFB();
						greType = CSGrenadeType.FB;
						break;
					case 1:
						possible = player.canThrowHE();
						greType = CSGrenadeType.HE;
						break;
					case 2:
						possible = player.canThrowSG();
						greType = CSGrenadeType.SG;
						break;
					default:
						greType = null;
						possible = false;
					break;
					}
					if (possible) {
						player.getPath().addGrenadeEvent(
								new CSGrenadeEvent(player.getCurrentPosition(),
										e.getPoint(),
										time,
										greType,
										player.getPath()));
						List<Line2D> temp = gui.getWorldPan().getGrenadeEvents();
						temp.add(new Line2D.Float(player.getCurrentPosition(),e.getPoint()));
						gui.getWorldPan().updateStaticLists(null, temp, null);
						stateChanged(null);
					}
					else
						JOptionPane.showMessageDialog(gui,
								new JLabel("Not enough "+greType+" !"));
				}
				// This is the case when we are adding a node to
				// an existing event :
				else {
					CSGrenadeEvent event = player.getPath().getGrenadeEventAt(time);
					if (event == null)
						return;
					event.appendPoint(e.getPoint());
					
					List<Line2D> temp = gui.getWorldPan().getGrenadeEvents();
					temp.add(new Line2D.Float(event.getNode(event.size()-2),e.getPoint()));
					gui.getWorldPan().updateStaticLists(null, temp, null);
					stateChanged(null);
				}
			}
			// If we are deleting a Gre event :
			if (state == CSControllerState.DELETINGGRE) {
				List<CSGrenadeEvent> events = player.getPath().getGrenadeEvents();
				int index = -1;
				int j = 0;
				while (j<events.size() && index == -1) {
					index = events.get(j).getPossibleSelectedIndex(e.getPoint());
					j++;
				}
				j--;
				
				if (index != -1) {
					player.getPath().getGrenadeEvents().get(j).deletePoint(index);
					
					if (index == 0)
						player.getPath().removeGrenadeEvent(events.get(j).getStartTime());

					// Updating the GUI :
					List<Line2D> temp = new ArrayList<Line2D>();
					for (int i=0; i<5; i++) {
						events = world.getPlayer(i).getPath().getGrenadeEvents();
						for (int k=0; k<events.size(); k++)
							temp.addAll(world.getPlayer(i).getPath().getGrenadeEvents().get(k).getLine2DArrayList());
					}

					gui.getWorldPan().updateStaticLists(null,
							temp,
							null);
					stateChanged(null);
				}
			}
		}
		// Any other button :
		else {
			if (selectedEntity == null)
				return;
			Modifier mod = null;
			if (selectedEntity instanceof CSPlayerPath)
				mod = new PlayerPathModifier((CSPlayerPath)selectedEntity,selectedIndex);
			if (selectedEntity instanceof CSLOSEvent)
				mod = new LOSEventModifier((CSLOSEvent)selectedEntity,gui.getTimelinePan().getEndTime(),this);
			if (selectedEntity instanceof CSGrenadeEvent)
				mod = new GrenadeEventModifier((CSGrenadeEvent)selectedEntity,gui.getTimelinePan().getEndTime(),this);

			JOptionPane.showMessageDialog(gui, mod);
			if (mod == null){
			    throw new AssertionError("mod is null");
			}
			mod.modify();
			completeStaticUpdate();
			stateChanged(null);
		}
	}

	/**
	 * This method is fired only when the user
	 * moves the timeline slider.
	 * 
	 * It is also used as a global render method.
	 */
	@Override
	public void stateChanged(ChangeEvent e) {
		double time = gui.getTimelinePan().getCurrentTime();
		
		if (time>0) {
			gui.getTimelinePan().setEditable(false);
			for (int i=0; i<5; i++)
				gui.getPlayerPan()[i].setEnabled(false);
			if (!playing)
				gui.getGrePan().setEnabled(true);
		}
		else {
			gui.getTimelinePan().setEditable(true);
			for (int i=0; i<5; i++)
				gui.getPlayerPan()[i].setEnabled(true);
			if (!playing)
				gui.getGrePan().setEnabled(false);
		}
		
		// LAUNCH THE CALC OF THE CURRENT SITUATION :
		List<ImagePoint2D> pts = new ArrayList<ImagePoint2D>();
		
		for (int i=0; i<5; i++) {
			if (i==gui.getSelected())
				pts.add(new ImagePoint2D(world.getPlayer(i).getPath().getPositionAt(gui.getTimelinePan().getCurrentTime()),Icons.getIcon("selectedPlayer"),world.getPlayer(i).getName()));
			else
				pts.add(new ImagePoint2D(world.getPlayer(i).getPath().getPositionAt(gui.getTimelinePan().getCurrentTime()),Icons.getIcon("player"),world.getPlayer(i).getName()));
			gui.getPlayerPan()[i].getFbPan().setCount(world.getPlayer(i).getFBs());
			gui.getPlayerPan()[i].getHePan().setCount(world.getPlayer(i).getHEs());
			gui.getPlayerPan()[i].getSgPan().setCount(world.getPlayer(i).getSGs());
		}
		
		gui.getWorldPan().updateDynamicLists(pts, null);
		// TODO ADD THE UPDATE OF STATIC LISTS (LOS/Gre)
		// TODO WHEN PLAYING THE STRAT !!
		// TODO TODO TODO TODO TODO TODO TODO TODO TODO
		if (playing) {
			List<Line2D> los = new ArrayList<Line2D>();
			for (int i=0; i<5; i++) {
				CSLOSEvent current = world.getPlayer(i).getPath().getLOSEventsAt(time);
				if (current != null)
					los.add(new Line2D.Float(world.getPlayer(i).getCurrentPosition(),
							current.getPosition()));
			}
			List<ImagePoint2D> gre = new ArrayList<ImagePoint2D>();
			for (int i=0; i<5; i++) {
				
				List<CSGrenadeEvent> temp = world.getPlayer(i).getPath().getAllGrenadeEventsAt(time);
				
				for (int j=0; j<temp.size(); j++) {
					CSGrenadeEvent current = temp.get(j);
					
					ImageIcon icon = Icons.getIcon("err");

					if (current != null) {
						if (current.getType() == CSGrenadeType.HE)
							icon = Icons.getIcon("he");
						if (current.getType() == CSGrenadeType.FB)
							icon = Icons.getIcon("fb");
						if (current.getType() == CSGrenadeType.SG)
							icon = Icons.getIcon("sg");
						gre.add(new ImagePoint2D(current.getPositionAtTime(time), icon));
					}
				}
			}

			gui.getWorldPan().updateStaticLists(new ArrayList<Line2D>(), new ArrayList<Line2D>(), los);
			gui.getWorldPan().updateSpecialLists(new ArrayList<Line2D>(), null, null);
			gui.getWorldPan().updateDynamicLists(null, gre);
			gui.getWorldPan().updateSpecialLists(new ArrayList<Line2D>(), new ArrayList<Line2D>(), new ArrayList<Line2D>());
		}
		else {
			// Building the active LOS/Grenade events list :
			List<Line2D> los = new ArrayList<Line2D>();
			List<Line2D> gre = new ArrayList<Line2D>();
			for (int i = filterActive ? gui.getSelected() : 0;
				 i<(filterActive ? gui.getSelected()+1 : 5);
				 i++) {
				CSPlayer player = world.getPlayer(i);
				CSLOSEvent losEvent = player.getPath().getLOSEventsAt(time);
				if (losEvent!=null)
					los.add(new Line2D.Double(losEvent, losEvent.getStartPosition()));
				CSGrenadeEvent greEvent = player.getPath().getGrenadeEventAt(time);
				if (greEvent!=null)
					gre.addAll(greEvent.getLine2DArrayList());
			}
			gui.getWorldPan().updateSpecialLists(null, gre, los);
		}
		
//		if (playing)
//			synchronized (this) {
//				this.notify();
//			} // TODO TODO TODO
	}

	public void focusGained(FocusEvent arg0) {}

	// This method is called only when a player's name
	// field has lost the focus :
	// We update then the player from the world database.
	public void focusLost(FocusEvent e) {
		PlayerPanel source = (PlayerPanel)((JComponent)e.getSource()).getParent().getParent();
		world.getPlayer(source.getId()).setName(source.getPlayersName());
	}

}
